home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Development Kits / MPW etc / MPW-GM / MPW / Examples / CFMExamples / ModApp / ToolLoader.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-12-03  |  14.2 KB  |  530 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        ToolLoader.c
  3.  
  4.     Contains:    Routines to load, unload, and list all available tools
  5.  
  6.     Written by:    Richard Clark, Alan Lillich
  7.  
  8.     Copyright:    © 1993-1995 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.                  8/15/94    BLS        Modified to deal with CFM-68K.  Support multiple
  13.                                      peff containers in a file.
  14.  
  15.                  1/13/94    RC        Added calls to parse a cfrg resource and
  16.                                      thereby determine whether we have 68K or PowerPC
  17.                                     code in the data fork or a resource. Also added
  18.                                     code to support loading PowerPC tools from
  19.                                     emulated 68K code.
  20.                                     
  21.                 11/28/93    RC        Extracted from Muslin, and simplified
  22.     To Do:
  23. */
  24.  
  25.  
  26. #ifndef __FILES__
  27.     #include <Files.h>
  28. #endif
  29.  
  30. #ifndef __RESOURCES__
  31.     #include <Resources.h>
  32. #endif
  33.  
  34. #ifndef __MEMORY__
  35.     #include <Memory.h>
  36. #endif
  37.  
  38. #ifndef __ERRORS__
  39.     #include <Errors.h>
  40. #endif
  41.  
  42. #ifndef __MENUS__
  43.     #include <Menus.h>
  44. #endif
  45.  
  46. #ifndef __OSUTILS__
  47.     #include <OSUtils.h>
  48. #endif
  49.  
  50. #ifndef __TEXTUTILS__
  51.     #include <TextUtils.h>
  52. #endif
  53.  
  54. #ifndef __MIXEDMODE__
  55.     #include <MixedMode.h>
  56. #endif
  57.  
  58. #include <CodeFragments.h>
  59.  
  60.  
  61. #include "ToolAPI.h"
  62. #include "ModApp.h"
  63. #include "Prototypes.h"
  64. #include "cfrg.h"
  65.  
  66. enum {
  67.         // Possible locations for our code
  68.         kUnknown,
  69.         kBuiltin,
  70.         kInResource,
  71.         kInDataFork
  72.     };
  73.     
  74. enum { kToolResType = 'TOOL' };
  75.     
  76. enum {k68kToolResID = 0,
  77.       kPPCToolResID = 1};
  78.  
  79.  
  80. enum {
  81.     rToolFolderNameSTR = 1001
  82.      };
  83.  
  84.  
  85. // === Local prototypes
  86. static OSErr GetFolderDirID (short volID, long parentDirID, StringPtr folderName, long* subFolderDirID);
  87. static void GetLoaderInfo (FSSpec *toolSpec, short *codeType, short *codeLocation, long *codeOffset);
  88.  
  89. // -------- Global variables which are used only by this file
  90. static short            toolFolderVol    = 0;
  91. static long                toolFolderDirID = 0;
  92.  
  93. void InitToolLoader(void)
  94. {
  95.     // This routine is responsible for locating the tools sub-folder in the same folder as our application.
  96.     // It needs to be called early in the initialization routine, before some other routine has had a chance
  97.     // to change the default directory.
  98.     OSErr            err;
  99.     StringHandle    toolFolderName;
  100.     long            appDirID;
  101.     
  102.     // Get the volume and folder information for this application
  103.     // ??? Would it be better to get the app's resRefNum and look up the volume & dirID that way?
  104.     err = HGetVol(NULL, &toolFolderVol, &appDirID);
  105.     if (err) return;                                 // HGetVol failed?!
  106.  
  107.     // Now, locate the subfolder in this folder
  108.     toolFolderName = GetString(rToolFolderNameSTR);
  109.     if (toolFolderName == NULL) return;             // No folder name, so abort
  110.     HLock((Handle)toolFolderName);
  111.     err = GetFolderDirID (toolFolderVol, appDirID, (StringPtr)*toolFolderName, &toolFolderDirID);
  112.     // Ignore the error, because it just indicates that the folder wasn't found. FindTools will
  113.     // alert the user if this is the case.
  114.     ReleaseResource((Handle)toolFolderName);
  115. } /* InitToolLoader */
  116.  
  117.  
  118. OSErr FindNamedTool (Str31 toolName, FSSpec *toolSpec)
  119. // Find the named tool in our list of available tools
  120. {
  121.     OSErr    err;
  122.     
  123.     err = FSMakeFSSpec(toolFolderVol, toolFolderDirID, toolName, toolSpec);
  124.     return err;
  125. } /* FindNamedTool */
  126.  
  127.  
  128. void GetToolNames (ToolInstallProcPtr InstallNameCallback)
  129. // Iterate over the list of installed tools, calling the InstallNameCallback for each
  130. {
  131.     FSSpec            currTool;
  132.     CInfoPBPtr        myCPB;           /* for the PBGetCatInfo call */
  133.     Str31            fileName;
  134.     short            index;
  135.     OSErr            err;
  136.     
  137.     if ((toolFolderVol == 0) || (toolFolderDirID == 0)) {
  138. //        AlertUser(kNeedsToolFolder);
  139. // <<< Disable the tools menu?
  140.         return; // Something wasn't initialized properly
  141.     }
  142.             
  143.     // Walk through the folder, calling the callback for each file
  144.     myCPB = (void*)NewPtrClear(sizeof(CInfoPBRec));
  145.     err = MemError();
  146.     if (err) goto done;
  147.     *fileName = '\0';    /* Empty the name */
  148.     myCPB->hFileInfo.ioNamePtr = (StringPtr)&fileName;
  149.     myCPB->hFileInfo.ioVRefNum = toolFolderVol;
  150.     index = 1;
  151.     err = noErr;
  152.     do {
  153.         myCPB->hFileInfo.ioFDirIndex= index;
  154.         myCPB->hFileInfo.ioDirID= toolFolderDirID;    /* we need to do this every time        */
  155.                                                     /* through, since GetCatInfo                */
  156.                                                     /* returns ioFlNum in this field        */
  157.         myCPB->hFileInfo.ioACUser= 0;                /* Clear the ioACUser byte if search is    */
  158.                                                     /* interested in it. Nonserver volumes won't  */
  159.                                                     /* clear it for you and the value returned is */
  160.                                                     /* meaningless. */
  161.         err = PBGetCatInfoSync(myCPB);
  162.         if (err == noErr) {
  163.             if (((myCPB->hFileInfo.ioFlAttrib & ioDirMask) == 0) && (myCPB->hFileInfo.ioFlFndrInfo.fdCreator == kCreatorCode)) {
  164.                 /* we have a file, check the file type & add it to our menu */
  165.                 if (myCPB->hFileInfo.ioFlFndrInfo.fdType == kToolType) {
  166.                     FSMakeFSSpec(toolFolderVol, toolFolderDirID, myCPB->hFileInfo.ioNamePtr, &currTool);
  167.                     InstallNameCallback(currTool);
  168.                 }
  169.             }
  170.         }
  171.         index++;    // Examine the next file
  172.     } while (err == noErr);
  173.     
  174. done:
  175.     DisposePtr((Ptr) myCPB);    // No need to check for a NULL pointer, as the Memory Mgr now does that
  176. } /* GetToolNames */
  177.  
  178.  
  179.     
  180. void GetLoaderInfo (FSSpec *toolSpec, short *codeType, short *codeLocation, long *codeOffset)
  181. // Determine what type of code we have, and whether the code is in the data fork
  182. // or in a resource.  The file may contain multiple pef containers, so we will
  183. // look for a best fit.
  184.  
  185. {
  186.     short    refNum = -1;
  187.     Handle    resourceScratch;
  188.     irHand    parsedResource;
  189.     short    bestFit = -1;        // index of best pef container found
  190.     int        i;
  191.     OSType  myCFMArch;
  192.     
  193.     *codeLocation = kUnknown;
  194.     *codeOffset = 0;
  195.  
  196.     if (GetCurrentISA() == kPowerPCISA)
  197.         myCFMArch = kPowerPCCFragArch;
  198.     else if (GetCurrentISA() == kM68kISA)
  199.         myCFMArch = kMotorola68KCFragArch;
  200.     else
  201.         DebugStr ((StringPtr) "\pUnknown CPU Architecture");
  202.  
  203.  
  204.     // We have to have a 'TOOL' resource and/or a 'cfrg' resource
  205.     // so open the resource file
  206.     refNum = FSpOpenResFile(toolSpec, fsRdPerm);
  207.     if (ResError() != noErr) goto done;
  208.     
  209.     // Do we have a 'TOOL' resource?
  210.     // PowerPC can work with either but 68K requires 68K.
  211.     // First grab the 68K resource if present
  212.  
  213.     resourceScratch = GetResource(kToolResType, k68kToolResID);
  214.     if (resourceScratch)
  215.     {
  216.         *codeLocation = kInResource;
  217.         *codeType = kM68kISA | kOld68kRTA;    
  218.     }
  219.     
  220.     // if we are PowerPC, lets see if we have a better choice
  221.     if (myCFMArch == kPowerPCCFragArch)
  222.     {
  223.         resourceScratch = GetResource(kToolResType, kPPCToolResID);
  224.         if (resourceScratch)
  225.         {
  226.             *codeLocation = kInResource;
  227.             *codeType = kPowerPCISA | kPowerPCRTA;    
  228.         }
  229.     }
  230.         
  231.  
  232.     if (gHasCFM) 
  233.     {
  234.         // Do we have a 'cfrg' resource? If so, it could take precedence 
  235.         // We'll look inside the 'cfrg' resource to get the code type and form
  236.         // If the CFRG has multiple items, pick the one that matches are ISA
  237.         
  238.         
  239.         parsedResource = (irHand) NewHandleClear(sizeof(internalResource));
  240.         
  241.         resourceScratch = Get1Resource(kCFragResourceType, kCFragResourceID);
  242.         if (ResError() || (resourceScratch == NULL)) goto done;
  243.         
  244.         if (parsedResource) 
  245.         {
  246.             short location;
  247.             
  248.             Parse_cfrg(resourceScratch, parsedResource);
  249.             
  250.             // We could check the "usage" field to see if this is a drop in
  251.             // (kIsDropIn == 2), but we won't bother, as the file type &
  252.             // creator was enough of a clue, and the file wouldn't be in
  253.             // the Modules menu if it wasn't a drop-in
  254.             
  255.             // Find the PEF container to match our CFM architecture
  256.             
  257.             for (i = 0; i < (*parsedResource)->itemCount; i++)
  258.             {
  259.                 if ((*parsedResource)->itemList[i].archType == myCFMArch)
  260.                     bestFit = i;
  261.         
  262.             }
  263.             
  264.             // use best found container            
  265.             if (bestFit >= 0)
  266.             {
  267.                 
  268.                 // Find out what type of code we have here
  269.                 if ((*parsedResource)->itemList[bestFit].archType == kPowerPCCFragArch)
  270.                     *codeType = kPowerPCISA | kPowerPCRTA;
  271.                 else 
  272.                     *codeType = kM68kISA | kCFM68kRTA;
  273.                 
  274.                 // Find out where the code is located
  275.                 location = (*parsedResource)->itemList[bestFit].location;
  276.                 if (location == 1 /* kOnDiskFlat */)
  277.                     *codeLocation = kInDataFork;
  278.                 else if (location == 0 /* kInMem */) 
  279.                     *codeLocation = kInResource;
  280.                 
  281.                 *codeOffset = (*parsedResource)->itemList[bestFit].codeOffset; 
  282.             }
  283.         
  284.             
  285.             DisposeHandle((Handle)parsedResource);
  286.         }
  287.     }
  288.  
  289. done:
  290.     if (refNum != -1)
  291.         CloseResFile(refNum);
  292. }
  293.  
  294.  
  295. static OSErr LoadFromResource(FSSpec *toolSpec, short codeType, WindowPtr wp, ToolStartupProcPtr *toolStartup)
  296. {
  297.     OSErr                err;
  298.     Handle                toolHandle;
  299.      short                refNum = -1;
  300.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  301.     short                resNum;
  302.  
  303.     Str255                errName;
  304.     CFragConnectionID    connID;
  305.  
  306.     // Get the tool. 
  307.     // of 1
  308.     if (codeType == kPowerPCISA | kPowerPCRTA)
  309.         resNum = kPPCToolResID;
  310.     else
  311.         resNum = k68kToolResID;
  312.     
  313.     refNum = FSpOpenResFile(toolSpec, fsCurPerm);
  314.     if ((err = ResError()) == noErr) {
  315.         // Get the tool and detach it from the resource file
  316.         toolHandle = GetResource(kToolResType, resNum);
  317.         if ((err = ResError()) == noErr) {
  318.             DetachResource(toolHandle);
  319.             MoveHHi(toolHandle); HLock(toolHandle);
  320.             
  321.             if (codeType == kPowerPCISA | kPowerPCRTA) {
  322.                 // This is PowerPC code, so it must be "prepared"
  323.                 err = GetMemFragment((Ptr)*toolHandle, 0, toolSpec->name, kPrivateCFragCopy, &connID, (Ptr*)toolStartup, errName);
  324.                 if (err) {
  325.                     DebugStr(errName);
  326.                     goto done;
  327.                 }
  328.     
  329.             } else {
  330.                 // This is 68K code, so just dereference the handle
  331.                 connID = 0;
  332.                 *toolStartup = (ToolStartupProcPtr)*toolHandle;
  333.             }
  334.         }
  335.     }
  336.     if (refNum != -1)                                    // Close the file if open
  337.         CloseResFile(refNum);
  338.         
  339.     // Fill in some information in the DrawingWindowPeek record
  340.     aWindow->toolResource = toolHandle;
  341.     aWindow->connectionID = connID;    
  342.     aWindow->toolLocation = kInResource;
  343.     aWindow->toolSpec = *toolSpec;
  344.         
  345. done:    
  346.     return err;
  347. }
  348.  
  349.  
  350. static OSErr LoadViaCFM (FSSpec *toolSpec, WindowPtr wp, ToolStartupProcPtr *toolStartup, long codeOffset)
  351. {
  352.     // We've discoverd that our code is in the data fork of the fil, so ask the Code Fragment
  353.     // Manager to load it.
  354.     
  355.     OSErr                err;
  356.     Str255                errName;
  357.     CFragConnectionID    connID;
  358.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  359.  
  360.     // Use the CFM to load the tool, and return its entry point and a Connection ID
  361.     if (!gHasCFM) {
  362.         err = kCFMNotPresent;
  363.         goto done;
  364.     }
  365.     
  366.     err = GetDiskFragment(toolSpec, codeOffset, 0, toolSpec->name, kPrivateCFragCopy, &connID, (Ptr*)toolStartup, errName);
  367.     if (err) {
  368.         DebugStr(errName);
  369.         goto done;
  370.     }
  371.     
  372.     // Fill in some information in the DrawingWindowPeek record
  373.     aWindow->connectionID = connID;
  374.     aWindow->toolResource = NULL;
  375.     aWindow->toolLocation = kInDataFork;
  376.     aWindow->toolSpec = *toolSpec;
  377.  
  378. done:    
  379.     return err;
  380. }
  381.  
  382.  
  383. static OSErr InitializeTool(ToolStartupProcPtr toolStartup, WindowPtr wp)
  384. {
  385.     // Execute the tool's startup sequence, which will fill in the table of routine pointers
  386.     OSErr    err;
  387.     
  388.     if (toolStartup == NULL)
  389.         return kGenericError;
  390.     
  391.     err = CallToolStartupProc (toolStartup, wp);
  392.         
  393.     return err;
  394. }
  395.  
  396. OSErr LoadTool (FSSpec *toolSpec, WindowPtr wp)
  397. // Use the Code Fragment Manager to pull the specified tool into memory
  398. // and attch it to the window
  399. {
  400.     // Locate the named tool in the folder and load it up
  401.     Boolean                loadedOK = false;
  402.     OSErr                err;
  403.     short                 codeType;
  404.     short                 codeLocation;
  405.     long                codeOffset;
  406.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  407.     ToolStartupProcPtr    toolStartup;
  408.  
  409.     if (wp == NULL) return noErr;    // No window? Give up!
  410.     SetPort(wp);
  411.     // Determine if this tool should be pulled in via the CFM
  412.     GetLoaderInfo (toolSpec, &codeType, &codeLocation, & codeOffset);
  413.     aWindow->toolLocation = codeLocation;
  414.     aWindow->toolCodeType = codeType;
  415.     
  416.     switch (codeLocation) {
  417.         case kUnknown:
  418.             AlertUser(kCodeNotFound, 0);
  419.             goto done;
  420.         break;
  421.         
  422.         case kInResource:
  423.             err = LoadFromResource (toolSpec, codeType, wp, &toolStartup);
  424.         break;
  425.         
  426.         case kInDataFork:
  427.             err = LoadViaCFM(toolSpec, wp, &toolStartup, codeOffset);
  428.             
  429.         break;
  430.     }
  431.     
  432.     if (err == noErr) {
  433.         err = InitializeTool(toolStartup, wp);
  434.         aWindow->currMenuBar = GetMenuBar();
  435.     }
  436.     
  437.     if (err) {
  438.         // Let the user know if there was a problem in loading or initialization
  439.         AlertUser(0, err);
  440.         goto done;
  441.     }
  442.     
  443.     SetWTitle(wp, toolSpec->name);
  444.         
  445. done:
  446.     UseMenuBar(aWindow->currMenuBar);    // Make sure the menu handling tools know about our menu bar
  447.     return err;
  448. } /* LoadTool */
  449.  
  450.  
  451. OSErr UnloadTool (WindowPtr wp)
  452. // Remove this tool from memory (this assumes that the tool's shutdown routine has
  453. // been called already
  454. {
  455.     OSErr                err = noErr;
  456.     DrawingWindowPeek    aWindow = (DrawingWindowPeek)wp;
  457.  
  458.     if ((aWindow == NULL) || ((aWindow->connectionID == 0) && (aWindow->toolResource == NULL)))
  459.         return noErr;    // No window? Give up!
  460.  
  461.     if (aWindow->toolRoutines.shutdownProc)
  462.         CallToolShutdownProc(aWindow->toolRoutines.shutdownProc, wp);    // Call via Mixed Mode
  463.     
  464.     UseMenuBar(NULL);
  465.     if (aWindow->currMenuBar) {
  466.         DisposeHandle(aWindow->currMenuBar);
  467.         aWindow->currMenuBar = NULL;
  468.     }
  469.  
  470.     if (gHasCFM && (aWindow->connectionID)) {
  471.         // This tool was opened via the CFM, so close it via the CFM
  472.         CFragConnectionID    connID;
  473.         
  474.         connID = aWindow->connectionID;
  475.         err = CloseConnection(&connID);
  476.     }
  477.     
  478.     if (aWindow->toolLocation == kInResource)
  479.         // This tool was loaded in from a resource, so dump the handle
  480.         DisposeHandle((Handle)aWindow->toolResource);
  481.  
  482.     aWindow->toolRoutines.shutdownProc = NULL;
  483.     aWindow->toolRoutines.menuAdjustProc = NULL;
  484.     aWindow->toolRoutines.menuDispatchProc = NULL;
  485.     aWindow->toolRoutines.toolIdleProc = NULL;
  486.     aWindow->toolRoutines.toolUpdateProc = NULL;
  487.     aWindow->toolRoutines.toolClickProc = NULL;
  488.     aWindow->toolRoutines.toolWindowMovedProc = NULL;
  489.     aWindow->toolRoutines.toolWindowResizedProc = NULL;
  490.     aWindow->toolRoutines.toolWindowActivateProc = NULL;
  491.     
  492.     aWindow->connectionID = 0;
  493.     aWindow->toolResource = NULL;
  494.     aWindow->toolRefCon = 0;
  495.  
  496.     return err;
  497. } /* UnloadTool */
  498.  
  499.  
  500. // -------- Private routine used to scan a folder for a list of tool files    
  501.  
  502.  
  503. static OSErr GetFolderDirID (short volID, long parentDirID, StringPtr folderName,
  504.                        long* subFolderDirID)
  505.     CInfoPBPtr    myCPBPtr;           /* for the PBGetCatInfo call */
  506.     OSErr        err;
  507.     
  508.     myCPBPtr = (void*)NewPtrClear(sizeof(HFileInfo));
  509.     err = MemError();
  510.     if (err) goto done;
  511.     
  512.     myCPBPtr->hFileInfo.ioNamePtr     = folderName;
  513.     myCPBPtr->hFileInfo.ioVRefNum     = volID;
  514.     myCPBPtr->hFileInfo.ioFDirIndex    = 0;
  515.     myCPBPtr->hFileInfo.ioDirID        = parentDirID;
  516.         
  517.     err= PBGetCatInfoSync(myCPBPtr);
  518.     if (err == noErr) {
  519.         if ((myCPBPtr->hFileInfo.ioFlAttrib & ioDirMask) != 0) {   /* we have a directory */
  520.             *subFolderDirID = myCPBPtr->hFileInfo.ioDirID;
  521.         } else
  522.             err = dirNFErr;    /* If this isn't a directory, signal "not found" */
  523.     }
  524.  
  525. done:
  526.     DisposePtr((Ptr)myCPBPtr);
  527.     return err;
  528. }
  529.